use std::{fs, io, path::Path};

use colored::Colorize;
use rig::{completion::ToolDefinition, tool::Tool};
use serde::{Deserialize, Serialize};
use serde_json::json;
use walkdir::WalkDir;

#[derive(Deserialize)]
pub struct OperationArgs {
    path: String,
    search: String,
}

#[derive(Debug, thiserror::Error)]
#[error("Math error")]
pub struct GrepError;

#[derive(Deserialize, Serialize)]
pub struct GrepCmd {
    pub project_path: String,
}

impl Tool for GrepCmd {
    const NAME: &'static str = "grep";

    type Error = GrepError;
    type Args = OperationArgs;
    type Output = String;

    async fn definition(&self, _prompt: String) -> ToolDefinition {
        ToolDefinition {
            name: "grep".to_string(),
            description: "根据文件路径查找指定字符串，输出包含指定该字符串的文件路径。一次调用只能查找一个字符串".to_string(),
            parameters: json!({
                "type": "object",
                "properties": {
                    "path": {
                        "type": "string",
                        "description": "查找的地址"
                    },
                    "search": {
                        "type": "string",
                        "description": "待查找的单个字符串，不支持正则"
                    }
                }
            }),
        }
    }

    async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
        println!("{}", format!("grep {} {}", args.path, args.search).red());
        let search_path_result = Path::new(&self.project_path)
            .join(&args.path)
            .canonicalize();
        let Ok(search_path) = search_path_result else {
            return Ok("路径{}下搜索{}的结果\n```\n```".to_string());
        };

        let mut got_path: Vec<String> = Vec::new();

        for path_op in WalkDir::new(&search_path) {
            let Ok(path) = path_op else {
                continue;
            };

            let pathbuf: std::path::PathBuf = path.into_path();
            if pathbuf.is_file() {
                if pathbuf
                    .metadata()
                    .map(|m| m.len() < 10 * 1000000)
                    .unwrap_or(false)
                {
                    let Ok(filepath) = fs::canonicalize(pathbuf) else {
                        continue;
                    };
                    let Some(path_str) = filepath.to_str() else {
                        continue;
                    };
                    let Ok(content) = fs::read_to_string(&path_str) else {
                        continue;
                    };

                    if content.contains(&args.search) {
                        if let Some(path_after) = path_str.strip_prefix(&self.project_path) {
                            if path_after.starts_with("/") {
                                got_path.push(format!(".{}", path_after));
                            } else {
                                got_path.push(path_after.to_string());
                            }
                        }
                    }
                }
            }
        }
        Ok(format!(
            "路径{}下搜索{}的结果\n```\n{}\n```",
            args.path,
            args.search,
            got_path.join("\n")
        ))
    }
}

#[test]
pub fn test() {
    let rt = tokio::runtime::Runtime::new().unwrap();
    let op = GrepCmd {
        project_path: "/home/x/code/RuoYi/".to_string(),
    };
    let result = op.call(OperationArgs {
        path: "ruoyi-admin".to_string(),
        search: "ruoyi".to_string(),
    });
    let r = rt.block_on(result);
    println!("{}", r.unwrap());
}
